#define DEFAULT_SOURCES
#include "app_serial.h"
#include "log/log.h"
#include "app_bluetooth.h"
#include "app_message.h"

/**
 * @brief 设置比特率
 * @param device 串口设备
 * @param baud_rate 比特率
 * @return int 成功返回0，失败返回-1
 */
static int app_serial_setBaudRate(SerialDevice *device, BaudRate baud_rate)
{
    struct termios options;
    if (tcgetattr(device->super.fd, &options) < 0)
    {
        return -1;
    }
    device->baud_rate = baud_rate;
    cfsetispeed(&options, baud_rate);
    return tcsetattr(device->super.fd, TCSAFLUSH, &options);
}

/**
 * @brief 设置数据位
 * @param device 串口设备
 * @param data_bits 数据位
 * @return int 成功返回0，失败返回-1
 */
static int app_serial_setDataBits(SerialDevice *device, DataBits data_bits)
{
    struct termios options;
    if (tcgetattr(device->super.fd, &options) < 0)
    {
        return -1;
    }
    device->data_bits = data_bits;
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= data_bits;

    return tcsetattr(device->super.fd, TCSAFLUSH, &options);
}

/**
 * @brief 设置校验位
 * @param device 串口设备
 * @param parity 校验位
 * @return int 成功返回0，失败返回-1
 */
static int app_serial_setParity(SerialDevice *device, Parity parity)
{
    struct termios options;
    if (tcgetattr(device->super.fd, &options) < 0)
    {
        return -1;
    }
    device->parity = parity;

    options.c_cflag &= ~(PARENB|PARODD);
    options.c_iflag |= parity;

    return tcsetattr(device->super.fd, TCSAFLUSH, &options);
}

/**
 * @brief 设置停止位
 * @param device 串口设备
 * @param stop_bits 停止位
 * @return int 成功返回0，失败返回-1
 */
static int app_serial_setStopBits(SerialDevice *device, StopBits stop_bits)
{
    struct termios options;
    if (tcgetattr(device->super.fd, &options) < 0)
    {
        return -1;
    }
    device->stop_bits = stop_bits;

    options.c_cflag &= ~CSTOPB;
    options.c_cflag |= stop_bits;

    return tcsetattr(device->super.fd, TCSAFLUSH, &options);
}

/**
 * @brief 设置串口为原始模式
 * @param device 串口设备
 * @return int 成功返回0，失败返回-1
 */
static int app_serial_setRaw(SerialDevice *device)
{
    struct termios options;
    if (tcgetattr(device->super.fd, &options) < 0)
    {
        return -1;
    }
    cfmakerraw(&options);

    return tcsetattr(device->super.fd, TCSAFLUSH, &options);
}

/**
 * @brief 设置串口为阻塞模式
 * @param device 串口设备
 * @param mode 阻塞模式
 * @return int 成功返回0，失败返回-1
 */
static int app_serial_setBlock(SerialDevice *device, int mode)
{
    struct termios options;
    if (tcgetattr(device->super.fd, &options) < 0)
    {
        return -1;
    }
    if (mode)
    {
        options.c_cc[VMIN] = 1;
        options.c_cc[VTIME] = 0;
    }
    else
    {
        options.c_cc[VMIN] = 0;
        options.c_cc[VTIME] = 5;
    }

    return tcsetattr(device->super.fd, TCSAFLUSH, &options);
}

int app_serial_setConnectionType(SerialDevice *device, ConnectionType type)
{
    switch (type)
    {
    case CONN_BT:
        app_serial_setBlock(device, 0);
        app_serial_setBaudRate(device, BAUD_9600);
        tcflush(device->super.fd, TCIOFLUSH);
        if (app_bluetooth_checkStatus(&device->super) >=0)
        {
            if(app_bluetooth_setBaudRate(&device->super, BT_BAUD_115200) < 0)
            {
                return -1;
            }
            if(app_bluetooth_reset(&device->super) < 0)
            {
                return -1;
            }
            sleep(2);
        }
        app_serial_setBaudRate(device, BAUD_115200);
        tcflush(device->super.fd, TCIOFLUSH);

        device->super.funcs->post_read = app_bluetooth_postRead;
        device->super.funcs->pre_write = app_bluetooth_preWrite;
        break;
    case CONN_LORA:
    default:
        break;
    }

    return 0;
}

SerialDevice *app_serial_init(char *filename, BaudRate baud_rate)
{
    Device *superDevice = app_device_int(filename);
    SerialDevice *device = realloc(superDevice, sizeof(SerialDevice));

    app_serial_setBaudRate(device, baud_rate);
    app_serial_setDataBits(device, DB8);
    app_serial_setParity(device, NONE);
    app_serial_setStopBits(device, ONE);
    app_serial_setRaw(device);
    app_serial_setBlock(device, 1);

    tcflush(device->super.fd, TCIOFLUSH);

    return device;
}